package auth

import (
	"net/http"
	"strings"

	"iot-cloud-platform/internal/logger"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

// AuthMiddleware provides JWT authentication middleware
func AuthMiddleware(authService *AuthService) gin.HandlerFunc {
	return func(c *gin.Context) {
		// Get token from Authorization header
		authHeader := c.GetHeader("Authorization")
		if authHeader == "" {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header required"})
			c.Abort()
			return
		}

		// Check if it's a Bearer token
		bearerToken := strings.Split(authHeader, " ")
		if len(bearerToken) != 2 || bearerToken[0] != "Bearer" {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid authorization header format"})
			c.Abort()
			return
		}

		// Validate token
		claims, err := authService.ValidateToken(bearerToken[1])
		if err != nil {
			logger.GetLogger().WithError(err).Error("Token validation failed")
			c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid or expired token"})
			c.Abort()
			return
		}

		// Set user information in context
		c.Set("user_id", claims.UserID)
		c.Set("username", claims.Username)
		c.Set("email", claims.Email)
		c.Set("roles", claims.Roles)
		c.Set("permissions", claims.Permissions)
		c.Set("session_id", claims.SessionID)

		c.Next()
	}
}

// RequirePermission middleware checks if user has required permission
func RequirePermission(authService *AuthService, resource, action string) gin.HandlerFunc {
	return func(c *gin.Context) {
		userID, exists := c.Get("user_id")
		if !exists {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
			c.Abort()
			return
		}

		userUUID, ok := userID.(uuid.UUID)
		if !ok {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID format"})
			c.Abort()
			return
		}

		hasPermission, err := authService.CheckPermission(userUUID, resource, action)
		if err != nil {
			logger.GetLogger().WithError(err).Error("Permission check failed")
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Permission check failed"})
			c.Abort()
			return
		}

		if !hasPermission {
			c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
			c.Abort()
			return
		}

		c.Next()
	}
}

// RequireRole middleware checks if user has required role
func RequireRole(requiredRole string) gin.HandlerFunc {
	return func(c *gin.Context) {
		roles, exists := c.Get("roles")
		if !exists {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
			c.Abort()
			return
		}

		userRoles, ok := roles.([]string)
		if !ok {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid roles format"})
			c.Abort()
			return
		}

		hasRole := false
		for _, role := range userRoles {
			if role == requiredRole || role == "admin" {
				hasRole = true
				break
			}
		}

		if !hasRole {
			c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient role"})
			c.Abort()
			return
		}

		c.Next()
	}
}

// APIKeyMiddleware provides API key authentication
func APIKeyMiddleware(authService *AuthService) gin.HandlerFunc {
	return func(c *gin.Context) {
		// Get API key from header
		apiKey := c.GetHeader("X-API-Key")
		if apiKey == "" {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "API key required"})
			c.Abort()
			return
		}

		// Validate API key (implementation needed)
		// This would check against the APIKey model in the database
		// For now, we'll skip this implementation

		c.Next()
	}
}

// OptionalAuth middleware for endpoints that can work with or without authentication
func OptionalAuth(authService *AuthService) gin.HandlerFunc {
	return func(c *gin.Context) {
		authHeader := c.GetHeader("Authorization")
		if authHeader == "" {
			c.Next()
			return
		}

		bearerToken := strings.Split(authHeader, " ")
		if len(bearerToken) != 2 || bearerToken[0] != "Bearer" {
			c.Next()
			return
		}

		claims, err := authService.ValidateToken(bearerToken[1])
		if err != nil {
			c.Next()
			return
		}

		// Set user information in context
		c.Set("user_id", claims.UserID)
		c.Set("username", claims.Username)
		c.Set("email", claims.Email)
		c.Set("roles", claims.Roles)
		c.Set("permissions", claims.Permissions)
		c.Set("session_id", claims.SessionID)

		c.Next()
	}
}

// GetCurrentUser helper function to get current user from context
func GetCurrentUser(c *gin.Context) (uuid.UUID, bool) {
	userID, exists := c.Get("user_id")
	if !exists {
		return uuid.Nil, false
	}

	userUUID, ok := userID.(uuid.UUID)
	return userUUID, ok
}

// GetCurrentUserRoles helper function to get current user roles
func GetCurrentUserRoles(c *gin.Context) ([]string, bool) {
	roles, exists := c.Get("roles")
	if !exists {
		return nil, false
	}

	userRoles, ok := roles.([]string)
	return userRoles, ok
}

// GetCurrentUserPermissions helper function to get current user permissions
func GetCurrentUserPermissions(c *gin.Context) ([]string, bool) {
	permissions, exists := c.Get("permissions")
	if !exists {
		return nil, false
	}

	userPermissions, ok := permissions.([]string)
	return userPermissions, ok
}

// HasRole checks if current user has specific role
func HasRole(c *gin.Context, requiredRole string) bool {
	roles, ok := GetCurrentUserRoles(c)
	if !ok {
		return false
	}

	for _, role := range roles {
		if role == requiredRole || role == "admin" {
			return true
		}
	}

	return false
}

// HasPermission checks if current user has specific permission
func HasPermission(c *gin.Context, requiredPermission string) bool {
	permissions, ok := GetCurrentUserPermissions(c)
	if !ok {
		return false
	}

	for _, permission := range permissions {
		if permission == requiredPermission || permission == "admin:all" {
			return true
		}
	}

	return false
}
